package pap.test.fcPuGm;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.Semaphore;
import gov.nasa.jpf.jvm.Verify;


public class BufferManager {

	static BufferManager instance;
	int bufferSize;
	LinkedBlockingDeque<List<Body>> buffer;
	List<Body> currentBufferList;

	Semaphore availablePlaces, availableSteps;

	private BufferManager(int bufferSize) {
		Verify.beginAtomic();
		this.bufferSize = bufferSize;
		buffer = new LinkedBlockingDeque<List<Body>>();
		currentBufferList = new ArrayList<Body>();
		buffer.add(currentBufferList);

		// The first list is available yet
		availablePlaces = new Semaphore(bufferSize - 1);
		// The buffer is empty
		availableSteps = new Semaphore(0);
		Verify.endAtomic();
	}

	/**
	 * Buffer initialization
	 * 
	 * @param bufferSize
	 * @param timesRegister
	 */
	public static void init(int bufferSize) {
		instance = new BufferManager(bufferSize);
	}

	public void startNewStep() throws InterruptedException {
		
		// Release a permission to the consumer
		availableSteps.release();

		// Wait until the buffer is not full
		availablePlaces.acquire();
		
		assert(availableSteps.availablePermits() <= bufferSize);

		// Add a new list to the buffer
		currentBufferList = new ArrayList<Body>();
		buffer.add(currentBufferList);

	}

	/**
	 * Add a new body to the buffer. Only SimpleBody are accepted.
	 * 
	 * @param body
	 */
	public void addBody(SimpleBody body) {
		currentBufferList.add(body);
	}

	/**
	 * Retrieves and returns one step from the head of the buffer.
	 * 
	 * @return Step
	 * @throws InterruptedException
	 */
	public List<Body> getNextList() throws InterruptedException {
		
		// Release a permission to the producer
		availablePlaces.release();

		// Wait until the buffer is not empty
		availableSteps.acquire();

		return buffer.take();
	}

	/**
	 * Returns the instance of the buffer
	 * 
	 * @return instance
	 */
	public static BufferManager getInstance() {
		return instance;
	}

}